package com.hmjk.health.background;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.message.BasicHeader;

public class MultipartEntity implements HttpEntity {
	private final static String RANDOM_BASE = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	
	private final String NEW_LINE_STR = "\r\n";  
    private final String CONTENT_TYPE = "Content-Type: ";
    private final String CONTENT_DISPOSITION = "Content-Disposition: ";
    
    private final String TYPE_OCTET_STREAM = "application/octet-stream";
    private final String TYPE_TEXT_CHARSET = "text/plain; charset=UTF-8";
    private final String TYPE_JSON_CHARSET = "application/json; charset=UTF-8";
    
    private final byte[] BINARY_ENCODING = "Content-Transfer-Encoding: binary\r\n\r\n".getBytes();
    private final byte[] BIT_ENCODING = "Content-Transfer-Encoding: 8bit\r\n\r\n".getBytes();
    
	private String mBoundary = null; 
	private ByteArrayOutputStream mOutputStream = new ByteArrayOutputStream();  
	
	public MultipartEntity() {  
        mBoundary = generateBoundary();  
    }  
	
	public void close() throws IOException{
		mOutputStream.close();
	}
	
	private String generateBoundary(){ 
	    Random random = new Random();  
	    StringBuffer sb = new StringBuffer();  
	    sb.append("Boundary-");
	    for (int i = 0; i < 3; i++) {
	    	for(int j=0; j < 4; j++){
	        int number = random.nextInt(RANDOM_BASE.length());  
	        sb.append(RANDOM_BASE.charAt(number));  
	    	}
	    	sb.append("-");
	    }  
	    for(int j=0; j < 12; j++){
	       int number = random.nextInt(RANDOM_BASE.length());  
	       sb.append(RANDOM_BASE.charAt(number));  
	    }
	    return sb.toString();
		
	}
	
	private void writeFirstBoundary() throws IOException {  
        mOutputStream.write(("--" + mBoundary + "\r\n").getBytes());  
    }  
	
	private byte[] getContentDispositionBytes(String paramName, String fileName) {  
        StringBuilder stringBuilder = new StringBuilder();  
        stringBuilder.append(CONTENT_DISPOSITION + "form-data; name=\"" + paramName + "\"");  
   
        if (fileName.trim().isEmpty() == false) {  
            stringBuilder.append("; filename=\"" + fileName + "\"");  
        }  
  
        return stringBuilder.append(NEW_LINE_STR).toString().getBytes();  
    }
	
	private void closeSilently(Closeable closeable){  
        try{  
            if(closeable != null){  
                closeable.close();  
            }  
        } catch (final IOException e) {  
            e.printStackTrace();  
        }  
    }  
	
	private void writeToOutputStream(String paramName, byte[] rawData, String type,  
            byte[] encodingBytes,  
            String fileName) {  
        try {  
            writeFirstBoundary();  
            mOutputStream.write((CONTENT_TYPE + type + NEW_LINE_STR).getBytes());  
            mOutputStream.write(getContentDispositionBytes(paramName, fileName));  
            mOutputStream.write(encodingBytes);  
            mOutputStream.write(rawData);  
            mOutputStream.write(NEW_LINE_STR.getBytes());  
        } catch (final IOException e) {  
            e.printStackTrace();  
        }  
    }  
	
	public void addJsonPart(String paramName, String value){
		 writeToOutputStream(paramName, value.getBytes(), TYPE_JSON_CHARSET, BIT_ENCODING, "");
	}
	
	public void addStringPart(String paramName, String value) {  
        writeToOutputStream(paramName, value.getBytes(), TYPE_TEXT_CHARSET, BIT_ENCODING, "");  
    }  
	
	public void addBinaryPart(String paramName, byte[] rawData) {  
        writeToOutputStream(paramName, rawData, TYPE_OCTET_STREAM, BINARY_ENCODING, "no-file");  
    }  
	
	public void addFilePart(String key, File file) {  
        InputStream fin = null;  
        try {  
            fin = new FileInputStream(file);  
            writeFirstBoundary();  
            final String type = CONTENT_TYPE + TYPE_OCTET_STREAM + NEW_LINE_STR;  
            mOutputStream.write(getContentDispositionBytes(key, file.getName()));  
            mOutputStream.write(type.getBytes());  
            mOutputStream.write(BINARY_ENCODING);  
  
            final byte[] tmp = new byte[4096];  
            int len = 0;  
            while((len = fin.read(tmp)) != -1){
                mOutputStream.write(tmp, 0, len); 
            }
            mOutputStream.write(NEW_LINE_STR.getBytes());  
            mOutputStream.flush();  
        } catch (final IOException e) {  
            e.printStackTrace();  
        } finally {  
            closeSilently(fin);  
        }  
    }  
	
	public void addEnd() throws IOException{
		final String endString = "--" + mBoundary + "--\r\n";  
        // 写入结束符  
        mOutputStream.write(endString.getBytes());  
        mOutputStream.flush();
	}
		
	@Override
	public void consumeContent() throws IOException,UnsupportedOperationException {
		if (isStreaming()) {  
            throw new UnsupportedOperationException(  
                    "Streaming entity does not implement #consumeContent()");  
        }  
	} 

	@Override
	public InputStream getContent() throws IOException, IllegalStateException {
		return new ByteArrayInputStream(mOutputStream.toByteArray()); 
	}

	@Override
	public Header getContentEncoding() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public long getContentLength() {
		return mOutputStream.toByteArray().length;
	}

	@Override
	public Header getContentType() {
		return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + mBoundary);
	}

	@Override
	public boolean isChunked() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isRepeatable() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isStreaming() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void writeTo(OutputStream outstream) throws IOException {
		
        outstream.write(mOutputStream.toByteArray());  
		
	}

}
